package top.infopub.httpclient.proxy;


import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import top.infopub.httpclient.base.HttpBizRsp;
import top.infopub.httpclient.base.HttpEnum;
import top.infopub.httpclient.cmpt.HttpsAction;
import top.infopub.httpclient.cmpt.Request;
import top.infopub.httpclient.cmpt.Response;
import top.infopub.httpclient.cmpt.Site;


/**
 * HTTP 交互处理类
 * @author Awoke
 * @version 2018-8-27
 * @see HttpClientProxy
 * @since
 */
public final class HttpClientProxy {

    private static Logger logger = LoggerFactory.getLogger(HttpClientProxy.class);

    private static Site site = new Site();

    /**
     * 请求接口 (post uft-8 form)
     * map K=V 当V有可能含有url字符时，务必使用URLEncoder.encode
     * @param reqUrl  url
     * @param reqBodyMap 参数map
     * @return 
     * @see 
     */
    public static HttpBizRsp reqPostFormUTF8(String reqUrl, Map<String, String> reqBodyMap) {
        return req(reqUrl, HttpEnum.Method.POST, HttpEnum.CharSet.UTF_8, HttpEnum.MimeType.FORM,
            createLinkString(reqBodyMap));
    }
    
    /**
     * 请求接口 (get uft-8 form)
     * map K=V 当V有可能含有url字符时，务必使用URLEncoder.encode
     * @param reqUrl  url
     * @param reqBodyMap 参数map
     * @return 
     * @see 
     */
    public static HttpBizRsp reqGetFormUTF8(String reqUrl, Map<String, String> reqBodyMap) {
        return req(reqUrl, HttpEnum.Method.GET, HttpEnum.CharSet.UTF_8, HttpEnum.MimeType.FORM,
            createLinkString(reqBodyMap));
    }

    /**
     * 请求接口 (post uft-8 form)
     * K=V 当V有可能含有url字符时，务必使用URLEncoder.encode
     * @param reqUrl  url
     * @param reqBody 参数遵循 A=a&B=b格式
     * @return 
     * @see 
     */
    public static HttpBizRsp reqPostFormUTF8(String reqUrl, String reqBody) {
        return req(reqUrl, HttpEnum.Method.POST, HttpEnum.CharSet.UTF_8, HttpEnum.MimeType.FORM,
            reqBody);
    }

    /**
     * 请求接口 (post uft-8 json)
     * @param reqUrl  url
     * @param reqBody json字符串 
     * @return 
     * @see 
     */
    public static HttpBizRsp reqPostJsonUTF8(String reqUrl, String reqBody) {
        return req(reqUrl, HttpEnum.Method.POST, HttpEnum.CharSet.UTF_8, HttpEnum.MimeType.JSON,
            reqBody);
    }

    /**
     * 请求接口 </br>
     * 注意：reqBody如果为K=V 当V有可能含有url字符时，务必使用URLEncoder.encode
     * @param reqUrl    url
     * @param reqMethod 请求方法
     * @param charset  字符集
     * @param mimeType  mime
     * @param reqBody json字符串 或者 参数遵循 key1=value1&key2=value2格式  
     * @return 
     * @see 
     */
    public static HttpBizRsp req(String reqUrl, HttpEnum.Method reqMethod,
                                 HttpEnum.CharSet charset, HttpEnum.MimeType mimeType,
                                 String reqBody) {
        logger.info("request--{}--{}", reqUrl, reqBody);
        String encoding = charset.getCharset();
        StringEntity httpEntity = null;
        // form表单 KEY1=VALUE1&KEY2=VALUE2&..
        if (mimeType.equals(HttpEnum.MimeType.FORM)) {
            httpEntity = createUrlEncodedFormEntity(transferToMap(reqBody, encoding), encoding);
        }
        else {
            // Creates a StringEntity with the specified content and charset. The MIME type defaults to "text/plain".
            httpEntity = new StringEntity(reqBody, encoding);
        }
        Request request = new Request(reqUrl, reqMethod.getMethod(), encoding, httpEntity);
        // 设置Content-Type
        request.addHeader("Content-Type", contentType(mimeType.getMimeType(), encoding));
        // 请求http接口
        Response response = HttpsAction.getInstance().doRequest(request, site);
        if (response.isOk()) {
            String rawText = response.getRawText();
            logger.info("response--{}", rawText);
            return new HttpBizRsp(true, rawText);
        }
        else {
            logger.error("{}--网络通讯异常~", reqUrl);
            return new HttpBizRsp(false, "网络通讯异常~");
        }
    }

    /** 
     * 把数组所有元素，按照“K=V”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    private static String createLinkString(Map<String, String> params) {
        List<String> keys = new ArrayList<String>(params.keySet());
        String prestr = "";
        for (int i = 0; i < keys.size(); i++ ) {
            String key = keys.get(i);
            String value = params.get(key);
            // 拼接时，不包括最后一个&字符
            if (i == keys.size() - 1) {
                prestr = prestr + key + "=" + value;
            }
            else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }

    /**
     * 将URL key=value 解析成Map
     * @param urlKeyValue
     * @return 
     * @throws UnsupportedEncodingException 
     * @see
     */
    private static Map<String, String> transferToMap(String urlKeyValue, String charset) {
        Map<String, String> map = new HashMap<>();
        // 计算出字符串里有多少个&符号
        int sum = StringUtils.countMatches(urlKeyValue, "&");
        // 以&符号分隔  获得key=value
        String array[] = urlKeyValue.split("&");
        // 将分隔出来的字符串加上
        for (int i = 0; i < sum + 1; i++ ) {
            // 以=分隔
            String[] c = array[i].split("=");
            // 处理key= 类似情况(只有k没有v)
            if (array[i].endsWith("=")) {
                map.put(c[0], "");
            }
            else {
                // 这里不decode UrlEncodedFormEntity会进行encode
                map.put(c[0], c[1]);
                // map.put(c[0], URLDecoder.decode(c[1], charset));
            }
        }
        return map;
    }

    /** 
     * 把数组所有元素生成UrlEncodedFormEntity，消息体内容类似于“KEY1=VALUE1&KEY2=VALUE2&...”这种形式
     * @param params 需要排序并参与字符拼接的参数组
     * @param charset 
     * @return 
     */
    private static UrlEncodedFormEntity createUrlEncodedFormEntity(Map<String, String> params,
                                                                   String charset) {
        // list
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        BasicNameValuePair pair = null;
        List<String> keys = new ArrayList<String>(params.keySet());
        for (int i = 0, len = keys.size(); i < len; i++ ) {
            String key = keys.get(i);
            String value = params.get(key);
            pair = new BasicNameValuePair(key, value);
            nvps.add(pair);
        }
        try {
            return new UrlEncodedFormEntity(nvps, charset);
        }
        catch (UnsupportedEncodingException e) {
            logger.error("occur UnsupportedEncodingException", e);
        }
        return null;
    }

    /** 生成 contentType **/
    private static String contentType(String mime, String charset) {
        return mime + "; charset=" + charset;
    }

}
